package main

import (
	"fmt"
	"log"
	"time"

	"google.golang.org/protobuf/proto"

	pb "expeditionjourney/generated/proto"
)

// HeartbeatHandler 心跳消息处理器
type HeartbeatHandler struct{}

// HandleMessage 处理心跳消息
func (h *HeartbeatHandler) HandleMessage(client *Client, req *pb.RequestMsg) (*pb.ResponseMsg, error) {
	// 解析心跳请求
	heartbeatReq := &pb.ReqHeartbeatMessage{}
	if err := proto.Unmarshal(req.Body, heartbeatReq); err != nil {
		log.Printf("Failed to unmarshal heartbeat request: %v", err)
		return nil, err
	}

	// 更新客户端最后心跳时间
	client.lastHeartbeat = time.Now()
	// 重置心跳超时计时器
	if client.heartbeatTimer != nil {
		client.heartbeatTimer.Reset(60 * time.Second)
	}

	// 计算延迟（毫秒）
	clientTime := heartbeatReq.GetClientTime()
	serverTime := time.Now().UnixMilli()
	delay := int32(0)
	if clientTime > 0 {
		delay = int32(serverTime - clientTime)
	}

	// 创建心跳响应
	heartbeatResp := &pb.ResHeartbeatMessage{
		ServerTime: serverTime,
		Delay:      delay,
	}

	// 序列化心跳响应
	respBody, err := proto.Marshal(heartbeatResp)
	if err != nil {
		log.Printf("Failed to marshal heartbeat response: %v", err)
		return nil, err
	}

	// 创建响应消息
	resp := &pb.ResponseMsg{
		Cmd:       ProtoCmds.CmdHeartbeatResp,
		Seq:       req.Seq,
		Body:      respBody,
		ErrorCode: pb.ErrorCode_ErrorNone,
	}

	log.Printf("Heartbeat from client %s, delay: %dms", client.id, delay)
	return resp, nil
}

// LoginHandler 登录消息处理器
type LoginHandler struct{}

// HandleMessage 处理登录消息
func (h *LoginHandler) HandleMessage(client *Client, req *pb.RequestMsg) (*pb.ResponseMsg, error) {
	// 解析登录请求
	loginReq := &pb.ReqLoginMessage{}
	if err := proto.Unmarshal(req.Body, loginReq); err != nil {
		log.Printf("Failed to unmarshal login request: %v", err)
		return nil, err
	}

	// 处理登录逻辑
	log.Printf("Login request from client %s, login type: %d, token: %s",
		client.id, loginReq.GetLoginType(), loginReq.GetToken())

	// 模拟登录验证 (实际应用中应该连接到认证服务)
	loginSuccess := true
	var playerId int32 = 1000 + int32(time.Now().Unix()%1000) // 简单生成一个玩家ID
	var sessionId string

	// 处理会话ID - 按照备注，会话ID由客户端生成，服务器需要返回此ID
	if loginReq.GetSessionId() != "" {
		// 客户端提供了会话ID（重连场景）
		sessionId = loginReq.GetSessionId()
		log.Printf("Client %s reconnecting with client-generated session ID: %s", client.id, sessionId)

		// 在实际应用中，这里应该根据会话ID查找之前的会话状态
		// 例如：playerId = findPlayerIdBySessionId(sessionId)
	} else {
		// 没有提供会话ID，表示新登录
		// 注意：不再由服务器生成会话ID
		sessionId = "" // 返回空会话ID，表示服务器接受了登录但需要客户端生成会话ID
		log.Printf("New login for client %s, client should generate a session ID", client.id)
	}

	// 如果token为空或者验证不通过则登录失败
	if loginReq.GetToken() == "" {
		loginSuccess = false
		playerId = 0
		sessionId = ""
	}

	// 更新客户端状态
	if loginSuccess {
		client.isLoggedIn = true
		client.playerId = playerId
		client.sessionId = sessionId // 存储客户端发来的会话ID，不再由服务器生成
	}

	// 创建登录响应，返回与请求相同的会话ID
	loginResp := &pb.ResLoginMessage{
		LoginSuccess: loginSuccess,
		PlayerId:     playerId,
	}

	// 只有当客户端发送了会话ID或登录成功时才设置响应中的会话ID
	if sessionId != "" || loginSuccess {
		loginResp.SessionId = &sessionId
	}

	// 序列化登录响应
	respBody, err := proto.Marshal(loginResp)
	if err != nil {
		log.Printf("Failed to marshal login response: %v", err)
		return nil, err
	}

	// 创建响应消息
	resp := &pb.ResponseMsg{
		Cmd:       ProtoCmds.CmdLoginResp, // 使用自动生成的命令码
		Seq:       req.Seq,
		Body:      respBody,
		ErrorCode: pb.ErrorCode_ErrorNone,
	}

	if !loginSuccess {
		resp.ErrorCode = pb.ErrorCode_ErrorLoginTokenInvalid
	}

	log.Printf("Login %s for client %s, player ID: %d, session ID: %s",
		map[bool]string{true: "successful", false: "failed"}[loginSuccess],
		client.id,
		playerId,
		sessionId)

	return resp, nil
}

// 生成会话ID
func generateSessionId(clientId string, playerId int32) string {
	return fmt.Sprintf("%s_%d_%d", clientId, playerId, time.Now().UnixNano())
}
